package com.sprouting.personal.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.sprouting.personal.emums.ResultEnum;
import com.sprouting.personal.model.common.ResultJson;
import com.sprouting.personal.model.common.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

/**
 * @author ：LX
 * 创建时间： 2019/5/16. 14:33
 * 地点：广州
 * 目的: 全局异常处理类，只针对控制器层的异常
 *  *      当发生异常的时候，这个类会对指定的异常进行捕获，不让异常抛出到用户界面
 * 备注说明：
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 对于注解校验的进行统一的验证处理
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public ResultJson methodArgumentNotValidExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e){
        log.warn("1、数据校验不通过，请求路径：{},异常错误信息:{}", request.getRequestURI(), e.getMessage());
        return ResultJson.error(getErrorMsg(e));
    }

    /**
     * 对于注解校验的进行统一的验证处理
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public ResultJson methodArgumentNotValidExceptionHandler(HttpServletRequest request, BindException e){
        log.warn("2、数据校验不通过，请求路径：{},异常错误信息:{}", request.getRequestURI(), e.getMessage());
        return new ResultJson(getErrorMsg(e));
    }

    /**
     * 默认异常处理方法
     * @param e 错误
     * @return 返回异常请求路径和异常信息
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultJson defaulErrorHandler(HttpServletRequest request, Exception e){
        log.error("3、默认异常处理,异常请求路径：{}", request.getRequestURI());

        ResultJson resultJson = new ResultJson("");
        String errorMsg;
        if (e instanceof NotLoginException) {
            // 如果是未登录异常
            NotLoginException ee = (NotLoginException) e;
            log.error("[权限错误] 未登录或已过期,{}", ee.getMessage());
            resultJson.setMsg(ResultEnum.UNAUTHORIZED.getMsg());
            resultJson.setCode(ResultEnum.UNAUTHORIZED.getCode());

        } else if(e instanceof NotRoleException) {
            // 如果是角色异常
            NotRoleException ee = (NotRoleException) e;
            log.error("[权限错误] 无此角色：{}", ee.getRole());
            resultJson.setMsg(ResultEnum.PRECONDITION_FAILED.getMsg());
            resultJson.setCode(ResultEnum.PRECONDITION_FAILED.getCode());

        } else if(e instanceof NotPermissionException) {
            // 如果是权限异常,无权限
            NotPermissionException ee = (NotPermissionException) e;
            log.error("[权限错误] 无此权限：{}", ee.getPermission());
            resultJson.setMsg(ResultEnum.PRECONDITION_FAILED.getMsg());
            resultJson.setCode(ResultEnum.PRECONDITION_FAILED.getCode());

        } else {
            // 普通异常
            resultJson.setMsg(e.getMessage());
            log.error("3、异常错误信息:{}", request.getRequestURI(), e);
        }

        return resultJson;
    }


    /**
     * 默认的业务异常
     */
    @ExceptionHandler(value = ServiceException.class)
    @ResponseBody
    public ResultJson jsonErrorHandler(HttpServletRequest request, ServiceException e) {
        log.warn("4、异常请求路径：{},异常错误信息:{}", request.getRequestURI(), e.getMsg());
        return new ResultJson(e.getMsg());
    }

    /**
     * 最高级别的错误拦截，防止错误信息进入到前端
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = Throwable.class)
    @ResponseBody
    public ResultJson throwableHandler(HttpServletRequest request, Throwable e){
        log.error("6、异常请求路径：{},Throwable错误信息:{}", request.getRequestURI(), e);
        return new ResultJson("内部服务器错误");
    }

    /**
     * 权限异常，未登录
     * @param request 请求
     * @param e 错误信息
     * @return
     */
   @ExceptionHandler(value = NotLoginException.class)
   @ResponseBody
   public ResultJson handlerNotLoginException(HttpServletRequest request, NotLoginException e){
       log.error("7、权限异常, 请求路径:{},错误信息：{}", request.getRequestURI(), e.getMessage());
       String errorMsg;

       if (NotLoginException.NOT_TOKEN.equals(e.getType())){
           //未能从请求中读取到token
           errorMsg = NotLoginException.NOT_TOKEN_MESSAGE;

       } else if (NotLoginException.INVALID_TOKEN.equals(e.getType())){
           //已读取到token，但是token无效
           errorMsg = "请求令牌无效，请重新登录";

       } else if (NotLoginException.TOKEN_TIMEOUT.equals(e.getType())){
           //已读取到token，但是token已经过期
           errorMsg = "令牌已过期，请重新登录";

       } else if (NotLoginException.BE_REPLACED.equals(e.getType())){
           //已读取到token，但是token已被顶下线
           errorMsg = "用户已在其他地方登陆";

       } else if (NotLoginException.KICK_OUT.equals(e.getType())){
           //已读取到token，但是token已被踢下线
           errorMsg = "用户已被强制下线";

       } else {
           errorMsg = "当前会话未登录";
       }

       ResultJson resultJson = new ResultJson<>(errorMsg);
       resultJson.setCode(ResultEnum.UNAUTHORIZED.getCode());
       return resultJson;
   }


    /**
     * 提取出错误信息
     * @param e 错误
     * @return
     */
    private String getErrorMsg(Exception e){
        //获取错误验证结果
        //Map<String, String> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        if (e instanceof BindException){
            BindException bindException = (BindException) e;
            bindException.getBindingResult().getFieldErrors().forEach((error) -> {
                //取出错误数据并封装
                //map.put(error.getField(), error.getDefaultMessage());
                sb.append(error.getDefaultMessage());
                sb.append(",");
            });

        } else if (e instanceof MethodArgumentNotValidException){
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            exception.getBindingResult().getFieldErrors().forEach((error) -> {
                sb.append(error.getDefaultMessage());
                sb.append(",");
            });
        }

        //去除最后一个逗号
        return sb.deleteCharAt(sb.length() - 1).toString();
    }

}
